#include "guish.hpp"
#include <errno.h>
#include <fcntl.h>

ClientConnection *clientConnection = NULL;

ClientConnection::ClientConnection(int termInputFd, int termOutputFd)
	:inFd(termInputFd), outFd(termOutputFd)
{
	shell = NULL;
	connected = true;
}

void ClientConnection::setInteractiveShell(Shell *shell)
{
	this->shell = shell;
}

void ClientConnection::doExit()
{
	trace << "ClientConnection::doExit called\n"; //DEBUG
	
	RPCParams exitParams;
	clientConnection->call("exit", &exitParams);
	
	connected = false;
	flushTerminal();
}

void ClientConnection::hangUp()
{
	trace << "ClientConnection::hangUp called\n"; //DEBUG
	connected = false;
}

bool ClientConnection::isConnected()
{
	return connected;
}

bool ClientConnection::isIdle()
{
	return !terminalOutput.getLen();
}

void ClientConnection::prepareSelect(int priority, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int *maxfd)
{
	if(*maxfd < 2)
		*maxfd = 2;
	
	FD_SET(inFd, exceptfds);
	FD_SET(outFd, exceptfds);
	
	FD_SET(inFd, readfds);
	if(terminalOutput.getLen() > 0)
		FD_SET(outFd, writefds);
}

void ClientConnection::postSelect(int priority, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
{
	// Input from the terminal has arrived
	if(FD_ISSET(0, readfds) || FD_ISSET(0, exceptfds))
		pullFromTerminal();
	
	// The terminal is ready to accept output
	if(FD_ISSET(1, writefds) || FD_ISSET(1, exceptfds))
		pushToTerminal();
}

void ClientConnection::pullFromTerminal()
{
	char buf[8192];
	int bytesRead;
	do {
		bytesRead = read(inFd, buf, 8192);
		if(bytesRead > 0) {
			receivedData(bytesRead, buf);
		} else if(bytesRead==0) {
			// EOF
			hangUp();
		}
	} while(bytesRead > 0);
}

void ClientConnection::pushToTerminal()
{
	int bytesWritten = write(outFd, terminalOutput.getData(), terminalOutput.getLen());
	if(bytesWritten > 0) {
		terminalOutput.popFront(bytesWritten);
	}
}

void ClientConnection::flushTerminal()
{
	// Switch the output to blocking mode
	int oldFlags = fcntl(outFd, F_GETFL);
	fcntl(outFd, F_SETFL, oldFlags&~O_NONBLOCK);
	
	int bytesWritten;
	do {
		bytesWritten = write(outFd, terminalOutput.getData(), terminalOutput.getLen());
		if(bytesWritten > 0)
			terminalOutput.popFront(bytesWritten);
	} while(bytesWritten > 0);
	
	// Switch output back to nonblocking
	fcntl(outFd, F_SETFL, oldFlags);
}

void ClientConnection::windowResize(int jobid, int newWidth, int newHeight)
{
	ptyManager->resizeAll(newWidth, newHeight);
}

void ClientConnection::requestTabCompletion(string prefix, string suffix)
{
	pair<string,string> response = ::tabComplete(prefix, suffix);
	
	RPCParams tabCompleteParams;
		tabCompleteParams.set("prefix", prefix);
		tabCompleteParams.set("suffix", suffix);
	clientConnection->call("tabcomplete", &tabCompleteParams);
}

void ClientConnection::sendData(int len, const char *data)
{
	terminalOutput.append(len, data);
	eventManager->notify();
}

RPC_HANDLER(init)
{
	int job = params->getInt("job");
	int width = params->getInt("width");
	int height = params->getInt("height");
	shell->runLoginScript(job, width, height);
}

RPC_HANDLER(p)
{
	int job = params->getInt("job");
	string cmd = params->getString("cmd");
	int width = params->getInt("width");
	int height = params->getInt("height");
	shell->runCommand(job, cmd, width, height);
}

RPC_HANDLER(winch)
{
	int job = params->getInt("job");
	int width = params->getInt("width");
	int height = params->getInt("height");
	clientConnection->windowResize(job, width, height);
}

RPC_HANDLER(rtabcomp)
{
	string prefix = params->getString("prefix");
	string suffix = params->getString("suffix");
	clientConnection->requestTabCompletion(prefix, suffix);
}

RPC_HANDLER(signal)
{
	int jobid = params->getInt("job");
	string sig = params->getString("sig");
	
	Job *job = shell->findJob(jobid);
	if(job)
		job->signal(sig);
}

